import { creepNameToObj, objIDToObj, roomNameToObj } from "../baseTools"
import roomTool from "./roomTool"
import structureTool from "./structureTool"
import { creepRoleEnum, creepRuleEnum, creepRuleNameEnum, taskType } from "../../enum"
import taskManager from "../../modules/task/taskManager"

/**获取creep组成的数组统计的各种部件数量 
 * @param {Array} creeps creep组成的数组
 * @returns 返回类似于{'work':1,'carry':1,'move':1}的对象
 */
function getBodymoduleCount(creeps) {
    let bodymodul = {}
    if (!Array.isArray(creeps)) creeps = [].concat(creeps)
    if (creeps.length == 0) return bodymodul
    if (typeof (creeps[0]) === 'string') creeps = creeps.map(creepName => Game.creeps[creepName])
    creeps.forEach(creep => {
        if (!creep) return  // 跳过异常creep
        creep.body.map(body => {
            !bodymodul[body.type] ? bodymodul[body.type] = 1 : bodymodul[body.type] += 1
        })
    })
    return bodymodul
}
/**将creep推送到对应房间的统计表及更新目标建筑挂载的creep列表 */
function pushCreepToTargetCreepList(creepName, fistPoint = false) {
    if (typeof (creepName) != 'string') creepName = creepName.name  // 避免传入的是creep而不是creepName
    // 纳入对应房间的统计数据
    let creepMemory = Memory.creeps[creepName]
    let creepListMemory = Memory.rooms[creepMemory.targetPos.roomName]['creeps'][creepMemory.tag][creepMemory.role]
    if (creepListMemory.indexOf(creepName) == -1) {
        creepListMemory.push(creepName)
    }
    creepListMemory = [...new Set(creepListMemory)] // 去重
    // 更新挂载统计表
    if (creepMemory.targetID.length != 0) {
        // 一些不需要挂载的creep，例如builder
        if (![creepRuleNameEnum.ruleBuilder].includes(creepMemory.rule)) {
            let targetID = fistPoint ? creepMemory.targetID[0] : creepMemory.targetID[creepMemory.targetID.length - 1]
            let targetObj = Game.getObjectById(targetID)
            if (targetObj) {
                targetObj.checkSelf()
                let targetObjCreepListMemory = Memory.rooms[targetObj.pos.roomName]['structure'][targetObj.structureType][targetObj.id].creeps
                if (targetObjCreepListMemory.indexOf(creepName) == -1) {
                    targetObjCreepListMemory.push(creepName)
                }
                targetObjCreepListMemory = [...new Set(targetObjCreepListMemory)] // 去重
            }
        }
    }
}
/**清除creep对应的内存数据 */
function deleteCreepToTargetCreepList(creepName) {
    if (typeof (creepName) != 'string') creepName = creepName.name  // 避免传入的是creep而不是creepName
    // 对应房间的统计表
    let creepMemory = Memory.creeps[creepName]
    let creepListMemory = Memory.rooms[creepMemory.targetPos.roomName]['creeps'][creepMemory.tag][creepMemory.role]
    let index = creepListMemory.indexOf(creepName)
    if (index != -1) creepListMemory.splice(index, 1)
    // 更新挂载统计表
    if (Array.isArray(creepMemory.targetID)) {
        creepMemory.targetID.map(id => {
            let targetObj = Game.getObjectById(id)
            if (!targetObj) return
            if (!Memory.rooms[targetObj.pos.roomName]['structure'][targetObj.structureType]) return
            let targetObjMemory = Memory.rooms[targetObj.pos.roomName]['structure'][targetObj.structureType][targetObj.id]
            if (!targetObjMemory) return
            index = targetObjMemory.creeps.indexOf(creepName)
            if (index != -1) { targetObjMemory.creeps.splice(index, 1) }
        })
    }
    // 重置挂载的任务
    if (!Game.creeps[creepName] && creepMemory.currentTaskID) {
        let taskDeta = taskManager.getTaskByID(creepMemory.currentTaskID, null, null, false)
        if (!taskDeta) return   // 不存在对应任务，则跳过
        Object.assign(Memory.rooms[creepMemory.targetPos.roomName]['task']['taskDeta'][creepMemory.currentTaskID], {
            holderID: null,
            doing: false,
        })
        // 不同的任务类型有不同的重置内容，还需要特化处理
        switch (taskDeta.taskType) {
            case taskType.needResource:
                // 资源需求任务，如果是不等于0，则更新需求量，等于零则删除
                if (creepMemory['params']['count'] == 0) {
                    delete Memory.rooms[creepMemory.targetPos.roomName]['task']['taskDeta'][creepMemory.currentTaskID]
                } else {
                    Object.assign(Memory.rooms[creepMemory.targetPos.roomName]['task']['taskDeta'][creepMemory.currentTaskID]['taskParams'], { count: creepMemory['params']['count'] })
                }
                break;
            case taskType.build:
                console.log('对建筑任务进行特化重置处理的逻辑');
                break;
            default:
                break;
        }
    }
}

// #region 通用工作逻辑
/**获取指定资源
 * 1. 默认获取能量
 */
function creepGetSource(params) {
    let { creep, room, sourceType = RESOURCE_ENERGY } = params
    creep = creepNameToObj(creep)
    room = roomNameToObj(room)
    switch (sourceType) {
        case RESOURCE_ENERGY:
            _getEnergyBySource(creep, room)
            break;
        default:
            break;
    }
}
/**从能源矿获取资源 */
function _getEnergyBySource(creep, room = null) {
    creep = creepNameToObj(creep)
    if (!room) room = Game.rooms[creep.memory.state.targetPos.roomName]
    let creepStateMemory = creep.memory.state
    if (!creep.memory.state.isRuning) {
        // 未处于移动中，则更新路径
        let targetIDList = _getNearStoreID(creep) || _getNearSourceID(creep)
        // console.log(`获取资源目标ID列表` + targetIDList.join());
        if (!targetIDList || targetIDList.length == 0) {        // 不存在满足条件的目标            
            // TODO:让发布任务的obj休眠，然后creep切换回原角色
            return false
        }
        let targetDetaList = targetIDList.map(targetID => {
            let pathDeta = roomTool.findPathByAStar(creep.pos, Game.getObjectById(targetID), { creep: creep })
            return Object.assign({ 'id': targetID }, pathDeta)
        }).sort((a, b) => a.cost - b.cost)
        if (targetIDList[0].incomplete) {
            creep.memory.state.isBlock = true  // 切换到堵路用跳点算法来解决
        }
        // 关于creep的内存数据更新
        let targetObj = Game.getObjectById(targetDetaList[0].id)
        creep.memory.targetID.push(targetDetaList[0].id)
        creep.memory.state.isRuning = true
        creep.memory.state.path = roomTool.findPathByJump(creep.pos, targetObj.pos, { creep: creep, ignoreCreeps: true, isSerialize: true })
        // 关于targetObj的内存对象更新
        let targetMemory = targetObj.room.memory['structure'][targetObj.structureType][targetObj.id]
        if (targetMemory.creeps.indexOf(creep.name) == -1) targetMemory.creeps.push(creep.name)         // 避免重复
    } else {
        let targetObj = Game.getObjectById(creep.memory.targetID[creep.memory.targetID.length - 1])
        if (!targetObj) {
            creepStateMemory.isRuning = false
            return
        }
        let resultCode
        switch (targetObj.structureType) {
            case 'source':
            case 'mineral':
                resultCode = creep.harvest(targetObj)
                switch (resultCode) {
                    case OK:
                        if (creep.store.getFreeCapacity() == 0) {
                            creepStateMemory.isWorking = true
                            creepStateMemory.isRuning = false
                            creepStateMemory.isBlock = false
                            creepStateMemory.path = []
                            creepStateMemory.tick = 0
                            let targetObj = Game.getObjectById(creep.memory.targetID.pop())
                            targetObj.checkSelf()
                            structureTool.deleteCreepInObjCreepList(targetObj, creep.name)
                            creep.say('背包满了，开始工作')
                        }
                        break;
                    case ERR_NOT_IN_RANGE:
                        creepMoveExtesion(creep)
                        break;
                    case ERR_NO_BODYPART:
                        creep.say(creep.name + `使用没有Work部件,羞愧难耐,算了自裁吧`)
                        creep.suicide()
                        break;
                    default:
                        console.log(creep.name + `使用采集的逻辑可能出现问题了,错误编码${resultCode}`);
                        break;
                }
                break;
            case 'container':
                resultCode = creep.withdraw(targetObj, RESOURCE_ENERGY)
                switch (resultCode) {
                    case OK:
                        if (creep.store.getFreeCapacity() == 0) {
                            creepStateMemory.isWorking = true
                            let targetObj1 = Game.getObjectById()
                            creep.memory.targetID.pop()
                            targetObj1.checkSelf()
                            structureTool.deleteCreepInObjCreepList(targetObj1, creep.name)
                            creep.say('背包满了，开始工作')
                        }
                        break;
                    case ERR_NOT_ENOUGH_RESOURCES:
                        console.log(creep.name + '使用withdraw的目标中没有足够数量的资源,切换新的目标');
                        creepStateMemory.isWorking = true
                        creepIsNotRuning(creep)
                        break;
                    case ERR_FULL:
                        creepStateMemory.isWorking = true
                        creepIsNotRuning(creep)
                        let targetID = creep.memory.targetID.pop()
                        let targetObj = Game.getObjectById(targetID)
                        targetObj.checkSelf()
                        structureTool.deleteCreepInObjCreepList(targetObj, creep.name)
                        creep.say('背包满了，开始工作')
                        break;
                    case ERR_NOT_IN_RANGE:
                        creepMoveExtesion(creep)
                        break
                    default:
                        if (creep.store.getFreeCapacity() == 0) {
                            creepStateMemory.isWorking = true
                            creepStateMemory.isRuning = false
                            creepStateMemory.isBlock = false
                            creepStateMemory.path = []
                            creepStateMemory.tick = 0
                            let targetObj = Game.getObjectById(creep.memory.targetID.pop())
                            targetObj.checkSelf()
                            structureTool.deleteCreepInObjCreepList(targetObj, creep.name)
                            creep.say('背包满了，开始工作')
                        }
                        console.log(creep.name + `使用withdraw逻辑可能出现问题了,错误编码${resultCode}`);
                        break;
                }
                break;
            default:
                console.log(creep.name + '获取资源逻辑出现异常，且暂时没写关于从' + targetObj.structureType + '获取资源逻辑');
                // creep.suicide()
                break;
        }
    }
}
/**获取离creep目标最近的仓库ID列表 */
function _getNearStoreID(creep) {
    creep = creepNameToObj(creep)
    let containerMemory = creep.room.memory['structure']['container']
    let containerIDs = Object.keys(containerMemory).filter(containerID => {
        let obj = Game.getObjectById(containerID)
        if (!obj) return false // 存在进度条，说明是工地
        if ('progress' in obj && obj.progress != obj.progressTotal && obj.structureType != STRUCTURE_CONTROLLER) return false
        if (obj.store.getFreeCapacity() < obj.store.getCapacity() - 50) return true
        return false
    })
    if (containerIDs.length == 0) return false
    return containerIDs
}
/**获取离creep最近的能源矿ID列表 */
function _getNearSourceID(creep) {
    creep = creepNameToObj(creep)
    let sourcesMemory = creep.room.memory['structure']['source']
    let targetIDs = Object.keys(sourcesMemory).filter(souceID => { if (sourcesMemory[souceID]['acceptCreep']) return true })    // 根据资源库获取
    if (targetIDs.length == 0) {
        // TODO: 添加一个休眠或者其他逻辑
        creep.say('没有可用能源点')
        return false
    }
    return targetIDs
}

/**根据任务池获取任务 */
function creepGetTask(creep, taskPool) {
    creep = creepNameToObj(creep)
    creep = creepNameToObj(creep)
    let result = { 'taskID': null, 'taskType': null }
    taskPool.map(taskType => {
        // console.log(`${creep.name}寻找${taskType}类型的任务}？`);
        if (result['taskID']) return
        result = taskManager.getTaskByType(taskType, creep.id, creep.pos.roomName)
        result['taskType'] = taskType
    })
    return result
}
// #endregion

// #region 通用移动逻辑
/**根据内存中的路径进行移动，无路径则使用生成路径，堵路则使用moveTo移动到目标点
 * fistPoint默认为false：默认移动目标是targetID的最后一个
*/
function creepMoveExtesion(creep, fistPoint = false) {
    creep = creepNameToObj(creep)
    let creepStateMemory = creep.memory.state
    let targetID = fistPoint ? creep.memory.targetID[0] : creep.memory.targetID[creep.memory.targetID.length - 1]
    let targetObj = Game.getObjectById(targetID)
    // console.log(creepStateMemory.isBlock?"堵路":'畅通');
    if (creepStateMemory.isBlock) {
        // 堵路，直接使用跳点算法
        creep.moveTo(targetObj.pos)
        if (creep.pos.getRangeTo(targetObj.pos) <= 1) creep.memory.state.isBlock = false
    } else {
        if (creepStateMemory.isRuning) {
            creep.moveByPath(creepStateMemory.path)
            creepStateMemory.isBlock = _isPathBlock(creep)
        } else {
            // console.log('creep的移动逻辑' + JSON.stringify(creep));
            // let pathDeta = roomTool.findPathByAStar(creep.pos, targetObj, { creep: creep })
            let pathDeta = roomTool.findPathByJump(creep.pos, targetObj.pos, { creep: creep, ignoreCreeps: true, isSerialize: true })

            // 关于creep的内存数据更新
            creep.memory.state.isRuning = true
            creep.memory.state.path = pathDeta
            // 关于targetObj的内存对象更新
            pushCreepToTargetCreepList(creep.name, fistPoint)
        }
    }
}
/**判断是否堵路 */
function _isPathBlock(creep) {
    creep = creepNameToObj(creep)
    if (creep.memory.state.tick >= Math.floor(Math.random() * 3) + 3) {
        creep.memory.state.tick = 0
        // console.log(JSON.stringify(creep.name) + JSON.stringify(creep.memory.state));
        let pos = creep.memory.state.pos
        if (!pos) {
            creep.memory.state.pos = creep.pos
            return false
        }
        if (creep.pos.isEqualTo(new RoomPosition(pos.x, pos.y, pos.roomName))) return true
        creep.memory.state.pos = creep.pos
    }
    creep.memory.state.tick += 1
    return false
}

/**判断当前creep是否已经到达目标房间，未到达目标房间使用moveTo移动到目标房间 */
function isTargetPos(creep) {
    let creepMemory = creep.memory
    if (creep.pos.roomName != creepMemory.targetPos.roomName) {
        // 先移动到目标房间
        creep.moveTo(new RoomPosition(creepMemory.targetPos.x, creepMemory.targetPos.y, creepMemory.targetPos.roomName))
        return false
    }
    return true
}
// #endregion

//#region  其他逻辑
/**根据任务id切换creep的rule */
function changeCreepRuleBytask(creep, taskID, params = {}) {
    creep = creepNameToObj(creep)
    let { taskDeta = null } = params
    let creepParams = {}
    if (!taskDeta) taskDeta = taskManager.getTaskByID(taskID, null, null, false)
    // 根据任务类型切换不同的角色
    switch (taskDeta.taskType) {
        case taskType.needResource:
            Object.assign(creepParams, {
                targetID: [taskDeta.publisherID],
                rule: creepRuleNameEnum.ruleCarrier,
            })
            break;
        case taskType.build:
            Object.assign(creepParams, {
                targetID: taskDeta.taskParams.targetID,
                rule: creepRuleNameEnum.ruleBuilder,
            })
            break;
        case taskType.repaire:
            Object.assign(creepParams, {
                targetID: taskDeta.taskParams.targetID,
                rule: creepRuleNameEnum.ruleRepaire,
            })
            break;
        default:
            console.log('切换角色异常,' + taskDeta.taskType + '的任务类型未配置对应的角色规则');
            break;
    }
    Object.assign(creepParams, {
        tag: 'worker',
        role: creep.memory.role != creepRoleEnum.worker.freeman ? creepRoleEnum.worker.worker : creepRoleEnum.worker.freeman,
        params: taskDeta.taskParams
    })
    changeCreepRule(creep, creepParams)
    return true
}
/**切换creep的rule,默认切换为freeman */
function changeCreepRule(creep, changParams = {}) {
    let { tag = null, role = null, rule = null, targetID = [], targetPos = null, params = null } = changParams
    // console.log(`${creep.name}执行的规则是${creep.memory.rule}`);
    creep = creepNameToObj(creep)
    // 删除原来挂载的数据
    deleteCreepToTargetCreepList(creep)
    // 重置creep自身的数据
    let targetObj = Game.getObjectById(targetID[0])
    Object.assign(creep.memory, {
        tag: tag || 'worker',
        role: role || creepRoleEnum.worker.freeman,
        rule: rule || creepRuleNameEnum.ruleFreeman,
        targetID: targetID,
        targetPos: targetPos || !targetObj ? creep.room.controller.pos : targetObj.pos,
        state: {
            isWorking: false,
            isRuning: false,
            isBlock: false,
            tick: 0,
            path: []
        },
        params: params || {}
    })
    // 重置creep相关的数据
    pushCreepToTargetCreepList(creep)
}
/**判断creep当前行动力与移动力的关系,关于疲劳值和移动路线的选择
* 1. 存在负疲劳值则优先走道路
* 2. 无疲劳值则走平原
 */
function creepIsTired(creep) {
    let isTired = true
    let moveCout = 0
    let tiredCount = 0
    creep = creepNameToObj(creep)
    let bodyTypeObj = getBodymoduleCount(creep.name)
    if (creep.store.getUsedCapacity() == 0) {
        // 空载状态，carry不纳入计算
        Object.keys(bodyTypeObj).filter(bodyType => bodyType != CARRY).map(bodyType => {
            bodyType == MOVE ? moveCout += bodyTypeObj[bodyType] : tiredCount += bodyTypeObj[bodyType]
        })
    } else {
        // 非空载状态，carry纳入计算
        Object.keys(bodyTypeObj).map(bodyType => {
            bodyType == MOVE ? moveCout += bodyTypeObj[bodyType] : tiredCount += bodyTypeObj[bodyType]
        })
    }
    // 产出大于消耗则走平原
    moveCout >= tiredCount ? isTired = false : isTired = true
    return isTired
}
/**切换creep的isRuning为false，也就是停下脚步
 * 1. isSleep = false
 * 2. isRuning = false;isBlock = false;path = null;tick = 0
 */
function creepIsNotRuning(creep) {
    creep = creepNameToObj(creep)
    creep.memory.state.isSleep = false
    creep.memory.state.isRuning = false
    creep.memory.state.isBlock = false
    creep.memory.state.path = null
    creep.memory.state.tick = 0
}
//#endregion

export {
    getBodymoduleCount,
    pushCreepToTargetCreepList,
    deleteCreepToTargetCreepList,
    creepGetTask,
    creepGetSource,
    creepMoveExtesion,
    changeCreepRule,
    changeCreepRuleBytask,
    creepIsTired,
    creepIsNotRuning,
    isTargetPos,
}